package ldh.im;

import java.util.concurrent.*;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

public class ImFuture<V> implements Future<V> {

    private ReentrantLock reentrantLock;
    private final Condition hasResult;

    private boolean isDone = false;
    private boolean isCancel = false;
    private V result;
    private Throwable throwable = null;

    public ImFuture() {
        this.reentrantLock = new ReentrantLock();
        hasResult = reentrantLock.newCondition();
    }

    public void setSuccess(V v) {
        final ReentrantLock lock = this.reentrantLock;
        lock.lock();
        try {
            isCancel = false;
            isDone = true;
            result = v;
            hasResult.signal();
        } finally {
            lock.unlock();
        }
    }

    public void setThrowable(Throwable throwable) {
        final ReentrantLock lock = this.reentrantLock;
        lock.lock();
        try {
            isDone = true;
            throwable = throwable;
            hasResult.signal();
        } finally {
            lock.unlock();
        }
    }

    @Override
    public boolean cancel(boolean mayInterruptIfRunning) {
        final ReentrantLock lock = this.reentrantLock;
        lock.lock();
        try {
            isCancel = true;
            isDone = true;
            hasResult.signal();
        } finally {
            lock.unlock();
        }
        return isCancel;
    }

    @Override
    public boolean isCancelled() {
        return isCancel;
    }

    @Override
    public boolean isDone() {
        return isDone;
    }

    @Override
    public V get() throws InterruptedException {
        if (isDone()) {
            return result;
        }
        final ReentrantLock lock = this.reentrantLock;
        lock.lockInterruptibly();
        try {
            while (!isDone) {
                hasResult.await();
            }
            return result;
        } finally {
            lock.unlock();
        }
    }

    @Override
    public V get(long timeout, TimeUnit unit) throws InterruptedException, TimeoutException {
        long nanos = unit.toNanos(timeout);
        final ReentrantLock lock = this.reentrantLock;
        lock.lockInterruptibly();
        try {
            while (!isDone) {
                if (nanos <= 0L) throw new TimeoutException("超时");
                nanos = hasResult.awaitNanos(nanos);
            }
            return result;
        } finally {
            lock.unlock();
        }
    }

    public Throwable getThrowable() {
        return throwable;
    }
}
